package com.sqi.reactive.common.token.impl

import com.auth0.jwt.JWT
import com.auth0.jwt.JWTVerifier
import com.auth0.jwt.algorithms.Algorithm
import com.auth0.jwt.interfaces.DecodedJWT
import com.fasterxml.jackson.databind.ObjectMapper
import com.sqi.reactive.common.auth.AuthPayload
import com.sqi.reactive.common.auth.MicroSubject
import com.sqi.reactive.common.auth.Subject
import com.sqi.reactive.common.auth.ThirdSystemSubject
import com.sqi.reactive.common.token.TokenHelper
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
import java.io.UnsupportedEncodingException
import java.util.*

@Component
class JWTTokenHelper : TokenHelper {
    private val secret = "opb-jwt-auth-secret"
    private var log = LoggerFactory.getLogger(this.javaClass)
    private lateinit var algorithm: Algorithm
    private lateinit var verifier: JWTVerifier
    override fun generate(subject: Subject): String {
        return this.generate(AuthPayload.of("user", subject))
    }

    override fun generate(subject: MicroSubject): String {
        return this.generate(AuthPayload.of("micro", subject))
    }

    override fun generate(subject: ThirdSystemSubject): String {
        return this.generate(AuthPayload.of("thirdsystem", subject))
    }

    override fun generate(subject: AuthPayload): String {
        val tokenId = UUID.randomUUID().toString().replace("-", "")

        return JWT.create()
                .withIssuer("opb")
                .withIssuedAt(Date())
                .withJWTId(tokenId)
                .withClaim("type", subject.type)
                .withClaim("subject", ObjectMapper().writeValueAsString(subject.subject))
                .sign(algorithm)
    }

    override fun verify(token: String?): AuthPayload? {
        if (token == null || token.isEmpty()) {
            return null
        }
        return try {
            val jwt = verifier.verify(token)
            val type = jwt.getClaim("type").asString()
            val strSubject = jwt.getClaim("subject").asString()
            when (type) {
                "user" -> {
                    return AuthPayload.of(type, ObjectMapper().readValue(strSubject, Subject::class.java))
                }
                "micro" -> {
                    return AuthPayload.of(type, ObjectMapper().readValue(strSubject, MicroSubject::class.java))
                }
                "thirdsystem" -> {
                    return AuthPayload.of(type, ObjectMapper().readValue(strSubject, ThirdSystemSubject::class.java))
                }
                else -> null
            }
        } catch (exp: Exception) {
            null
        }
    }

    override fun tokenId(token: String): String {
        val jwt: DecodedJWT
        var jwtId: String? = null
        try {
            jwt = verifier.verify(token)
            jwtId = jwt.id
        } catch (exp: Exception) {
        }
        return jwtId!!
    }

    init {
        try {
            algorithm = Algorithm.HMAC256(secret)
            verifier = JWT.require(algorithm).build()
        } catch (e: IllegalArgumentException) {
            //FIXME: I don't think it will happen,because all of the parameters are passed by myself
        } catch (e: UnsupportedEncodingException) {
            //FIXME:I don't think it will happen,because all of the parameters are passed by myself
        }
        object : Thread() {
            //初次加载jwt会比较慢，预热加载调用一次
            override fun run() {
                try {
                    val s = System.currentTimeMillis()
                    generate(Subject())
                    val e = System.currentTimeMillis()
                    log.info("jwt 预先初始化成功，耗时: " + (e - s) + " ms")
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
        }.start()
    }
}
